using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Text;
using System.Windows.Forms;

using Microsoft.TeamFoundation.WorkItemTracking.Common;
using Microsoft.TeamFoundation.WorkItemTracking.Controls;
using Microsoft.TeamFoundation.WorkItemTracking.Client;
using System.Collections.Specialized;
using System.Drawing.Imaging;
using System.IO;


namespace UKVSTS.WorkItemControls
{
    public abstract class BaseWorkItemControl : UserControl, IWorkItemControl, IWorkItemToolTip
    {
        // Fields used to store any data that gets passed to
        // us by the WI control infrastructure.
        //

        // The work item we are editing.
        //
        private WorkItem m_DataSource;

        // The fields in the WorkItem that we are bound to.
        //
        private String m_FieldName;

        // A dictionary of properties that have been passed in from
        // the WIT definition file.
        //
        private StringDictionary m_Properties;

        // Just incase we need any other services.
        //
        private IServiceProvider m_Site;

        // Whether we are read only or editable.
        //
        private bool m_ReadOnly;


        // Standard colours for our UI
        //
        protected static readonly Color HighlightBackColor = SystemColors.Info;
        protected static readonly Color HighlightForeColor = SystemColors.InfoText;
        protected static readonly Color ErrorBackColor = Color.Red;
        protected static readonly Color ErrorForeColor = Color.Pink;


        // Tootip support 
        //
        private ToolTip m_ToolTip;
        private Label m_Label;


        // Common Error displaying code
        //
        private bool m_InError;
        private String m_ErrorDescription;


        // Events required by IWorkItemControl
        //
        public event EventHandler AfterUpdateDatasource;
        public event EventHandler BeforeUpdateDatasource;


        public virtual void Clear()
        {
        }

        public virtual void FlushToDatasource()
        {
            
        }

        /// <summary>
        /// This is called to tell us that the data source and the field
        /// we are bound to have changed. we need to updated our status based 
        /// on this.
        /// </summary>
        public void InvalidateDatasource()
        {
            OnDataSourceChanged ();
            ResetStyle ();
        }

        /// <summary>
        /// Override this function to populate the contol with data from
        /// the work item that we are bound to.
        /// </summary>
        public virtual void OnDataSourceChanged ()
        {

        }

        /// <summary>
        /// Dictinary of values that is passed in from the WorkItem Type definition
        /// </summary>
        public StringDictionary Properties
        {
            get { return m_Properties; }
            set
            {
                m_Properties = value;
                OnPropertiesSet ();
                ResetStyle();
            }
        }

        /// <summary>
        /// Overide this function if you want to be notified when the
        /// Properties dictionary is updated.
        /// </summary>
        protected virtual void OnPropertiesSet ()
        {

        }

        public bool ReadOnly
        {
            get { return m_ReadOnly; }
            set { m_ReadOnly = value; }
        }

        public void SetSite(IServiceProvider serviceProvider)
        {
            m_Site = serviceProvider;
        }

        protected IServiceProvider ServiceProvider
        {
            get { return m_Site; }
        }

        public object WorkItemDatasource
        {
            get { return m_DataSource; }
            set
            {
                m_DataSource = (WorkItem)value;
                ResetStyle();
            }
        }

        protected WorkItem WorkItem
        {
            get { return this.m_DataSource; }
        }
 

        protected abstract void ResetStyle();

        public string WorkItemFieldName
        {
            get { return m_FieldName; }
            set
            {
                m_FieldName = value;
                ResetStyle();
            }
        }

        protected Field Field  
        {
            get
            {
                if (m_DataSource != null && !String.IsNullOrEmpty (m_FieldName))
                {
                    return m_DataSource.Fields[m_FieldName];
                }
                return null;
                }
        }

        protected bool HasValidField
        {
            get
            {
                return true;
            }
        }

        protected void SetFieldValue (Object value)
        {
            if (Field.FieldDefinition.IsEditable)
            {
                // TODO
                //
                // Can we do a better check to see if the value has actually changed?
                //
                if ((Field.Value != null) || (value != null))
                {
                    if (BeforeUpdateDatasource != null)
                    {
                        BeforeUpdateDatasource (this, EventArgs.Empty);
                    }
                    // Update the actual field value
                    //
                    Field.Value = value;

                    if (AfterUpdateDatasource != null)
                    {
                        AfterUpdateDatasource (this, EventArgs.Empty);
                    }
                }
            }
        }

        protected void SetFieldValue (String stringValue)
        {
            Object actualValue = null;
            switch (Field.FieldDefinition.FieldType)
            {
                case FieldType.DateTime:
                    actualValue = DateTime.Parse (stringValue);
                    break;

                case FieldType.Double:
                    actualValue = Double.Parse (stringValue);
                    break;

                case FieldType.Integer:
                    actualValue = Int32.Parse (stringValue);
                    break;

                case FieldType.PlainText:
                case FieldType.String:
                    actualValue = stringValue;
                    break;

                case FieldType.History:
                case FieldType.Html:
                case FieldType.TreePath:
                default:
                    ThrowCantConvertFromError (stringValue);
                    break;
            }
            
            SetFieldValue (actualValue);
        }

        private void ThrowCantConvertFromError (object source)
        {
            throw new ArgumentException (
                String.Format ("Can't convert from a {0} to a {1}",
                source.GetType ().Name, Field.FieldDefinition.FieldType.ToString ()));
        }

        protected void SetToolTip()
        {
            if ((Label != null) && (ToolTip != null))
            {
                String text;
                if (InError)
                {
                    text = m_ErrorDescription;
                }
                else
                {
                    text = Field.FieldDefinition.HelpText;
                }
                ToolTip.SetToolTip(Label, text);
            }
        }
        
        public Label Label
        {
            get { return m_Label; }
            set { m_Label = value; }
        }

        public ToolTip ToolTip
        {
            get { return m_ToolTip; }
            set { m_ToolTip = value; }
        }

        //public abstract void Clear ();

        //public abstract void FlushToDatasource ();

        //public abstract void InvalidateDatasource ();

        public String ErrorDescription
        {
            get { return m_ErrorDescription; }
        }

        public bool InError
        {
            get { return m_InError; }
        }

        protected void SetControlInError (String desc)
        {
            m_InError = true;
            m_ErrorDescription = desc;
        }

        protected void ClearControlInError ()
        {
            m_InError = false;
            m_ErrorDescription = null;
        }
    }
}
